import Dep from "./observe/Dep"
import { observe } from "./observe/index"
import Watcher from "./observe/Watcher"
import nextTick from "./utils/nextTick"

export function initState(vm) {
    
    let opts = vm.$options
    // 以下数据就是属性的初始化 先后顺序
    // 会有考点，props,methods,data,computed,watch
    if(opts.props){
        initProps()
    }

    
    if(opts.methods){
        initMethods()
    }

    if(opts.data){
        initData(vm)
    }

    if(opts.computed){
        initComputed(vm)
    }

    if(opts.watch){
        initWatch(vm)
    }

}

function initProps() {
    
}

// 初始化vue中的data
function initData(vm) {
    // 面试题， data可能是对象也可能是函数 ？ 为什么？ 是对象怎么处理，是函数怎么处理
    let data = vm.$options.data;
    // 是函数的时候，直接执行函数。但是执行的时候，注意this的指向问题
    data = vm._data = typeof data === 'function'? data.call(vm) : data;
   
    // data代理
    // 将data上的数据代理到vm上
    for (const key in data) {
        proxy(vm,'_data',key)
    }

    observe(data)
   
}

function initWatch(vm) {
    let watch = vm.$options.watch
    for (const key in watch) {
       let handler = watch[key]
       if(Array.isArray(handler)){ //数组
        handler.forEach(item=>{
            createrWatcher(vm,key,item)
        })
       } else { // 对象/字符串/函数
        createrWatcher(vm,key,handler)
       }
    }
}


// vm.$watch(()=>{return 'a'}) 这个a就是watch上的属性
function createrWatcher(vm,exprOrfn,handler,options) {
    if(typeof handler === 'object'){
        // name: {
        //     handler() {},
        // },
        options = handler
        handler = options.handler
    }
    if(typeof handler ==='string'){
        handler = vm[handler] //将实例上的方法作为handler
    }
    // 其他的都是函数
    // watch最终处理  通过$watch
    return vm.$watch(vm,exprOrfn,handler,options)
}

function initComputed(vm) {
    let computed = vm.$options.computed
    // 需要一个watcher
    let watcher = vm._comutedWatchers = {}
    // 讲computed属性 通过defineProperty进行处理
    for (let key in computed) {
    //   注意有两种方式 ：方法 和 对象
        let userDef = computed[key]
        // 获取get
        let getter = typeof userDef == 'function'?userDef:userDef.get()
        // 给计算属性的每一个属性都加添加一个watcher
        watcher[key] = new Watcher(vm,getter,()=>{},{lazy:true})
        
        // definedReactive
        defineComputed(vm,key,userDef) //计算属性中的watcher，加入lazy：trrue.
    }
}
function initMethods() {
    
}

function proxy(vm,source,key) {
    Object.defineProperty(vm,key,{
        get(){
            return vm[source][key]
        },
        set(newVal){
            vm[source][key] = newVal
        }
    })
}

let sharePropDefinition = {}
function defineComputed(target,key,useDef) {
    sharePropDefinition = {
        enumerable:true,
        configurable:true,
        get:()=>{},
        set:()=>{}
    }
    // useDef 是computed的:属性值
    // 它有两种情况:一种是函数 一种是对象
    // fullName() {
    //     return this.fristName + this.lastName;
    //   },

    // fullName:{
    //     get(){}
    //     set(){}
    // }
    if(typeof useDef =='function'){
        sharePropDefinition.get = createComputedGetter(key)
    } else {
        sharePropDefinition.get = createComputedGetter(key)
        sharePropDefinition.set = useDef.set
    }
    Object.defineProperty(target,key,sharePropDefinition)
}

// 高阶函数
function createComputedGetter(key) {
    return function () {
        // dirty 变量为真的话 就执行用户方法，假的话就不执行用户方法
        // dirty 这个变量在watcher中
        let watcher = this._comutedWatchers[key]
        if(watcher){
            if(watcher.dirty){ // 在watcher中
                // 执行计算属性，求值
                watcher.evaluate()
                // console.log(watcher.getter);
            }

            // 判断一下有没有渲染的watcher 有执行
            
            if(Dep.target){
                watcher.depend()
            }
            return watcher.value
        }
    }   
}

export function stateMixin(vm) {
    vm.prototype.$nextTick = function (cb) {
        nextTick(cb)
    }
    
    vm.prototype.$watch = function (Vue,exprOrfn,handler,options={}) {
        console.log(exprOrfn,handler,options);
        // 实现watch的原理就是new Watcher
        // 渲染的时候走的是  渲染的watcher 
        // $watch的时候走的是 watch的watcher
        // 他们的区别就是new Watcher(xxx,xxx,xxx,true/false),通过最后一个参数控制
        
        let watcher = new Watcher(Vue,exprOrfn,handler,{...options,user:true})
        
        if(options.immediate){
            handler.call(Vue)
        }
    }
}
